经典蓝牙和蓝牙BLE的区别

说起蓝牙,大家一定听过蓝牙1.0 2.0 3.0 4.0,不过现在已经不再用版本号区分蓝牙了,蓝牙1.0~3.0都是经典蓝牙,在塞班系统就已经开始使用了,确实很经典。有些人一直认为蓝牙4.0就是蓝牙BLE,其实是错误的。因为4.0是双模的,既包括经典蓝牙又包括低能耗蓝牙。经典蓝牙和蓝牙BLE虽然都是蓝牙,但其实还是存在很大区别的。蓝牙BLE相比于经典蓝牙的优点是搜索、连接的速度更快,关键就是BLE(Bluetooth Low Energy)低能耗,缺点呢就是传输的速度慢,传输的数据量也很小,每次只有20个字节。但是蓝牙BLE因为其低能耗的优点,在智能穿戴设备和车载系统上的应用越来越广泛。


蓝牙4.0BLE协议与协议栈的关系

  • 协议定义的是一系列的通信标准,通信双方需要共同按照这一标准进行正常的数据收发。

  • 协议栈是协议的具体实现形式,通俗的理解为用代码实现的函数库,以便于开发人员调用。

蓝牙4.0BLE协议栈就是将各个层定义的协议都集合在一起,以函数的形式实现,并提供一些应用层API,供用户调用。

注意:虽然协议是统一的,但是协议的具体实现形式是变化的,即不同厂商提供的协议栈是有区别的,例如:函数名称和参数列表可能有区别,选择协议栈以后,需要学习具体的例子,查看厂商提供的Demo演示程序、说明文档(通常,实现协议栈的厂商会提供一些API手册供用户查询)来学习各个函数的使用方式,进而快速地使用协议栈进行应用程序的开发工作。


如何使用蓝牙4.0BLE协议栈

既然蓝牙4.0BLE协议栈已经实现了蓝牙4.0BLE协议,那么用户就可以使用协议栈提供的API进行应用程序的开发,在开发过程中不必过多的关注蓝牙4.0BLE协议的具体实现细节,只需要关注一个核心的问题:应用程序数据从哪里来到哪里去。

至于调用协议栈中函数后,如何初始化应用进行数据发送等工作,蓝牙4.0BLE协议栈已经完成了所需要的初始化。

如果开发过程中确实需要或者是想要了解蓝牙4.0BLE协议,可以查看SIG提供的标准协议规范。

深入理解蓝牙4.0BLE协议栈

协议栈概述

我们以TI的CC254X系列BLE芯片为例来深入了解下蓝牙4.0BLE协议栈。TI的蓝牙4.0BLE协议栈包含两部分:主机和控制器。主机和控制器的分离要追溯到蓝牙BR/EDR设备时期,控制器和主机通常会分开实现。

协议栈的实现方式采用分层的思想,

  • 控制器部分包括:物理层、链路层、主机控制接口层;

  • 主机部分包括:逻辑链路控制及自适应协议层、安全管理层、属性协议层、通用访问配置文件层、通用属性配置文件层。

上层可以调用下层提供的函数来实现需要的功能。

BLE蓝牙协议栈结构低

功耗蓝牙协议栈包含两部分共8层:主机(Host)和控制器(Controller)。

控制器部分包括:

  • 物理层(Physical Layer)
  • 链路层(Link Layer)
  • 主机控制接口层(Host Controller Interface)

主机部分包括:

  • L2CAP 逻辑链路控制及自适应协议层(Logical Link Control and Adaptation Protocol)
  • 安全管理层(Security Manager)
  • ATT 属性协议层(Attribute Protocol)
  • GAP 通用访问配置文件层(Generic Access Profile)
  • GATT 通用属性配置文件层(Generic Attribute Profile)

蓝牙系统核心包括射频收发器,基带和协议栈。

核心系统协议 包括射频(RF)协议、链路控制(LC)协议、链路管理 (LM)协议、逻辑链路的控制和适配 (L2CAP)协议。

蓝牙核心系统最底三层是射频,链路控制,链路管理协议,通常会把这三者归为一个子系统——蓝牙控制器。把往上的其他层一起称为为蓝牙主机。在蓝牙控制器和蓝牙主机之间实现通信通常需要有主机-控制器接口,Host to Controller Interface(HCI)。蓝牙系统的具体应用apps,就是建立在蓝牙主机之上。而host部分由蓝牙软件厂商开发和维护,control部分由蓝牙的硬件厂商提供,两部分通过hci(主机控制器接口)进行通信和数据交互。

Direct Test Mode
厂商提供的测试模块,可以通过HCI或者串口直接控制蓝牙的物理层来让它收发数据包

Physical Layer(PHY)物理层
负责数据和语音的发送和接收,特点是短距离、低功耗。蓝牙天线一般体积小、重量轻,属于微带天线。
1Mbps自适应跳频GFSK(高斯频移键控),运行在免费的工业频段2.4GHz。

Link Layer(LL)链路层
LL层为RF控制器,控制设备处于准备(standby)、广播、监听/扫描(scan)、初始化、连接,这五种状态中一种。
五种状态切换描述为:未连接时,设备广播信息,另外一个设备一直监听或按需扫描,两个设备连接初始化,设备连接上了。
发起聊天的设备为主设备,接受聊天的设备为从设备,同一次聊天只能有一个意见领袖,即主设备和从设备不能切换。

Host-Controller Interface(HCI)主机控制器接口
HCI层为接口层,向上为主机提供软件应用程序接口(API),对外为外部硬件控制接口,可以通过串口、SPI、USB来实现设备控制。

L2CAP逻辑链路控制和适配协议
L2CAP层提供数据封装服务,允许逻辑上的点对点通讯。
基于包的协议,将包传输到HCI,对于无主机系统,就将包传给链路管理器LM。支持多路复用,包的分割和重组,以及向上层协议提交服务质量信息。

Security Manager(SM)安全管理
SM层提供配对和密匙分发,实现安全连接和数据交换。

Attribute Protocal(ATT)属性协议
ATT层负责数据检索,允许设备向另外一个设备展示一块特定的数据称之为属性,在ATT环境中,展示属性的设备称之为服务器,与它配对的设备称之为客户端。链路层状态(主机和从机)与设备的ATT角色是相互独立的。例如:主机设备既可以是ATT服务器,也可以是ATT客户端;从机设备既可以是ATT服务器,也可以是ATT客户端。

Generic Attribute Profile(GATT)通用属性协议
GATT层定义了使用 ATT 的服务框架和配置文件(profiles)的结构。BLE 中所有的数据通信都需要经过GATT。
它定义两个 BLE 设备通过叫做 Service 和 Characteristic 的东西进行通信。GATT 就是使用了 ATT(Attribute Protocol)协议,ATT 协议把 Service, Characteristic遗迹对应的数据保存在一个查找表中,次查找表使用 16 bit ID 作为每一项的索引。

当两个设备建立连接之后,它们就处于下面两种角色之一:
GATT服务器:为GATT客户端提供数据服务的设备。
GATT客户端:从GATT服务器读写应用数据的设备。

Generic Access Profile(GAP)通用访问协议
GAP直接与应用程序或配置文件(profiles)通信的接口,处理设备发现和连接相关服务。另外还处理安全特性的初始化。对上级,提供应用程序接口,对下级,管理各级职能部门,尤其是指示LL层控制室五种状态切换,指导保卫处做好机要工作。GAP给设备定义了若干角色,其中主要的两个是:外围设备(Peripheral)和中心设备(Central)。


PHY层,工作车间,1Mbps自适应跳频GFSK(高斯频移键控),运行在免证的2.4GHz

LL层为RF控制器,控制室,控制设备处于准备(standby)、广播、监听/扫描(scan)、初始化、连接,这五种状态中一种。五种状态切换描述为:未连接时,设备广播信息(向周围邻居讲“我来了”),另外一个设备一直监听或按需扫描(看看有没有街坊邻居家常里短可聊,打招呼“哈,你来啦”),两个设备连接初始化(搬几把椅子到院子),设备连接上了(开聊)。发起聊天的设备为主设备,接受聊天的设备为从设备,同一次聊天只能有一个意见领袖,即主设备和从设备不能切换。

 HCI层,为接口层,通信部,向上为主机提供软件应用程序接口(API),对外为外部硬件控制接口,可以通过串口、SPI、USB来实现设备控制。

L2CAP层,物流部,行李打包盒拆封处,提供数据封装服务

SM层,保卫处,提供配对和密匙分发,实现安全连接和数据交换

ATT层,库房,负责数据检索

GATT层,出纳/库房前台,出纳负责处理向上与应用打交道,而库房前台负责向下把检索任务子进程交给ATT库房去做,其关键工作是把为检索工作提供合适的profile结构,而profile由检索关键词(characteristics)组成。

GAP层,秘书处,对上级,提供应用程序接口,对下级,管理各级职能部门,尤其是指示LL层控制室五种状态切换,指导保卫处做好机要工作。

https://blog.csdn.net/ooakk/article/details/7302425

任务调度—OSAL操作系统抽象层

正如一个公司为了实现扩大产能和产品多样化,建立了多个办公室和工厂一样,蓝牙为了实现同多个设备相连,或实现多功能,也实现了功能扩充,这就产生了调度问题。因为,虽然软件和协议栈可扩充,但终究最底层的执行部门只有一个。

为了实现多事件和多任务切换,需要把事件和任务对应的应用,以及其相关的提供支撑“办公室”和“工厂”打包起来,并起一个名字OSAL操作系统抽象层,类似于集团公司以下的子公司。

设备改造—HAL硬件抽象层

如果实现软件和硬件的低耦合,使软件不经改动或很少改动即可应用在另外的硬件上,这样就方便硬件改造、升级、迁移后,软件的移植。HAL硬件抽象层正是用来抽象各种硬件的资源,告知给软件。其作用类似于嵌入式系统设备驱动的定义硬件资源的h头文件。其角色类似于现代工厂的设备管理部。

BLE低功耗蓝牙系统架构

BLE低功耗蓝牙软件有2个主要组成: OSAL操作系统抽象层和 HAL硬件抽象层,多个Task任务和事件在OSAL管理下工作,而每个任务和事件又包括3个组成:BLE 协议栈,profiles和应用程序。

蓝牙4.0BLE协议栈分层思想的优点

蓝牙4.0BLE协议栈采用分层思路的最大优点是:将服务、接口和协议这三个概念明确的区分开来。服务说明某一层为上一层提供了一些什么样的功能;接口说明上一层如何使用下一层的服务;而协议涉及到如何实现本层的服务。这样,各层之间就具有很强的独立性,当协议的一部分发送变化时,只需对与此相关的分层进行修改即可,其他分层不需要改变。

蓝牙4.0BLE协议栈开发平台配置

单一设备

网络处理器

OSAL操作系统抽象层

OSAL功能:协议栈中所谓的OSAL就是一个小型的操作系统,实现了最基本的任务轮询。

任务注册,初始化,启动
任务间的同步,互斥
中断处理
存储器分配管理
提供定时器功能

常用术语

  • 资源
  • 共享资源
  • 任务
  • 多任务运行
  • 内核
  • 互斥
  • 消息队列

OSAL运行机理

蓝牙4.0 BLE中事件和任务的处理采用方法是:

  • 建立事件表,保存各任务对应的事件,
  • 建立函数表,保存各任务事件处理函数的地址。

  • 通过不断的查询事件表来判断是否有事件发生,如果有事件发生,则查找函数表找到对应的事件处理函数对事件进行处理。

  • 事件表使用数组来实现,数组每一项对应一个事件的任务,每一位表示一个事件;
  • 函数表使用函数指针数组来实现,数组的每一项就是一个函数指针,指向了事件处理函数。

重要变量:

  • tasksCnt : 任务总个数
  • tasksEvents: 指向事件表的指针
  • taskArr : 函数指针数组,指向事件处理函数

各种初始化—运行操作系统—有任务触发—执行任务


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main(void)
{

HAL_BOARD_INIT();
InitBoard( OB_COLD );

HalDriverInit();
osal_snv_init();
osal_init_system();

HAL_ENABLE_INTERRUPTS();

InitBoard( OB_READY );

#if defined ( POWER_SAVING )
osal_pwrmgr_device( PWRMGR_BATTERY );
#endif

osal_start_system(); // No Return from here /启动系统

return 0;
}

主函数一进去就是各种系统初始化:包括硬件、GATT、 GAP 层、任务等的初始化。然后执行 osal_start_system();操作系统。我们重点关心2 个函数:

  • 初始化操作系统 osal_init_system()

  • 运行操作系统 sal_start_system()

osal_init_system()

系统初始化函数,进入函数。发现里面有 6个初始化函数,这里我们只关心osalInitTasks();任务初始化函数。继续由该函数进入,进入后发现终于看到各层任务的添加,taskID 依次递增表示优先级降低,即越底层优先级越高(LL、HAL、HCI、L2CAP、GAP、GATT、SM、Profiles、Application),最高层即应用层优先级最低,最后执行应用层的任务初始化。

第二个函数 osal_start_system();运行操作系统。同样用 go to definition 的方法进入该函数。再进入 osal_run_system() ,我们欣喜地发现这里就是任务轮询的基本轮廓,源码和分析如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
void osal_run_system( void )
{
uint8 idx = 0;

#ifndef HAL_BOARD_CC2538
osalTimeUpdate();
#endif

Hal_ProcessPoll();
//这段代码扫描触发的任务
do {
if (tasksEvents[idx]) //优先级高的任务被置位,说明有任务触发
{
break;//跳出任务扫描,得到的idx即为任务ID!
}
} while (++idx < tasksCnt);//idx从0开始递增,先查询高优先级的任务

if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
//然后进入临界保护区,提取事件后清清除
HAL_ENTER_CRITICAL_SECTION(intState);
events = tasksEvents[idx];
tasksEvents[idx] = 0; // 清除
HAL_EXIT_CRITICAL_SECTION(intState);
//然后通过函数指针调用对应的任务处理函数
activeTaskID = idx;
events = (tasksArr[idx])( idx, events );
activeTaskID = TASK_NO_TASK;
//taskArr[]即为函数指针数组,存放所有定义好的处理任务函数的入口地址
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events; // 保存未处理的事件
HAL_EXIT_CRITICAL_SECTION(intState);
}
#if defined( POWER_SAVING )//如果定义了节能模式
else
{
osal_pwrmgr_powerconserve(); //进入睡眠
}
#endif

#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
{
osal_task_yield();
}
#endif
}

OSAL消息队列

1
2
3
4
5
6
7
typeedef struct
{
void *next;
uint16 len;
uint8 dest_id;

} osal_msg_hdr_t;

从消息队列得到一个消息:

1
pMsg = osal_msg_receive( simpleBLETASKId )

OSAL添加新任务

  • 新任务的初始化函数
  • 新任务的事件处理函数

OSAL应用编程接口 API:

  • 消息管理
  • 任务同步
  • 时间管理
  • 中断管理
  • 任务管理
  • 内存管理
  • 电源管理
  • 非易失性闪存管理